"""

抽象实现的发送者，由不同的应用实现具体的方法

"""

from abc import ABC, abstractmethod
from error import MethodNotImplementedError,KeyConfigError
from cptools import LogHandler, INFO
from typing import (
    Dict,
    Optional,
    Union,
    Any,
    List,
    Tuple,
    Generator
)
from queue import Queue
from configparser import ConfigParser,NoSectionError,NoOptionError
from consts import(
    DEFAULT_KEY_PATH,
    MAX_QUEUE_SIZE,
    DEFAULT_MEDIA_API,
    ERROR_MESSAGES,
    BASE_API_URL
)
from pathlib import Path
import requests
import json
import time
from requests.exceptions import (
    HTTPError,
    ConnectionError,
    Timeout,
    RequestException
)
import logging

class Sender(ABC):
    """
    抽象类只定义行为
    """

    def send_text(self, *args, **kwargs):
        """
        发送文本消息
        """

    @abstractmethod
    def send_image(self, *args, **kwargs):
        """
        send image message
        :return:
        """

    @abstractmethod
    def send_voice(self, *args, **kwargs):
        """
        发送语音消息
        """

    @abstractmethod
    def send_video(self, *args, **kwargs):
        """
        发送视频消息
        """

    @abstractmethod
    def send_news(self, *args, **kwargs):
        """
        send news
        :return:
        """

    @abstractmethod
    def send_markdown(self, *args, **kwargs):
        """
        send markdown message
        :return:
        """

    @abstractmethod
    def send_file(self, *args, **kwargs):
        """
        send file
        :return:
        """

    @abstractmethod
    def send_card(self, *args, **kwargs):
        """
        发送卡片消息
        """

    @abstractmethod
    def send_taskcard(self, *args, **kwargs):
        """
        发送任务卡片消息
        """

    @abstractmethod
    def send_mpnews(self, *args, **kwargs):
        """
        发送mpnews图文消息
        :param args:
        :param kwargs:
        :return:
        """


class MsgSender(Sender):
    """
    封装公共属性以及方法
        Attributes:
        logger: 日志处理器
        queue: 消息队列，限制每分钟20条消息
        _webhook: 企业微信webhook地址
        headers: HTTP请求头
        errmsgs: 错误消息映射
        _media_api: 媒体文件API地址
        key_cfg: 配置文件解析器
        base_url: 企业微信API基础URL
        proxies: 代理设置
    """
    logger: LogHandler
    queue: Queue
    _webhook: Optional[str]
    headers: Optional[Dict[str, str]]
    errmsgs: Dict[str, str]
    _media_api: str
    key_cfg: ConfigParser
    base_url: str
    proxies: Optional[Dict[str, str]]

    def __init__(self, log_level=INFO, *args, **kwargs):
        super(MsgSender, self).__init__()
        self.logger = LogHandler(self.__class__.__name__, level=log_level)
        self.queue = Queue(20)
        self._webhook = None
        self.headers = None
        self.errmsgs = ERROR_MESSAGES
        self._media_api = DEFAULT_MEDIA_API
        self.key_cfg = ConfigParser()
        self.base_url = BASE_API_URL
        self.proxies = kwargs.get('proxies', None)

    def send_text(self, *args, **kwargs):
        """
        send text message
        :return:
        """
        raise MethodNotImplementedError

    def send_image(self, *args, **kwargs):
        """
        send image message
        :return:
        """
        raise MethodNotImplementedError

    def send_voice(self, *args, **kwargs):
        """
        发送语音消息
        """
        raise MethodNotImplementedError

    def send_video(self, *args, **kwargs):
        """
        发送视频消息
        """
        raise MethodNotImplementedError

    def send_news(self, *args, **kwargs):
        """
        send news
        :return:
        """
        raise MethodNotImplementedError

    def send_markdown(self, *args, **kwargs):
        """
        send markdown message
        :return:
        """
        raise MethodNotImplementedError

    def send_file(self, *args, **kwargs):
        """
        send file
        :return:
        """
        raise MethodNotImplementedError

    def send_card(self, *args, **kwargs):
        """
        发送卡片消息
        """
        raise MethodNotImplementedError

    def send_taskcard(self, *args, **kwargs):
        """
        发送卡片消息
        """
        raise MethodNotImplementedError

    def send_mpnews(self, *args, **kwargs):
        """
        发送mpnews图文消息
        :param args:
        :param kwargs:
        :return:
        """
        raise MethodNotImplementedError


    def _get_local_key(self,section:str,options:list[str],**kwargs) -> Generator[str, None, None]:
        """
        Args:
            section: 配置文件的节名称
            options: 需要获取的配置项列表
            **kwargs: 其他参数，包括自定义配置文件路径
        
        Yields:
            配置项的值
        
        Raises:
            KeyConfigError: 当配置项不存在时
            FileNotFoundError: 当配置文件不存在时
        """
        self.logger.info('You have not delivered a key parameter, try to get it from local files')
        key_path = Path(kwargs.get('key_path', DEFAULT_KEY_PATH)) 
        if key_path.is_file():
            self.key_cfg.read(key_path)
            try:
                for option in options:
                    yield self.key_cfg.get(section,option)
            except (NoSectionError, NoOptionError) as e:
                raise KeyConfigError
        else:
            raise FileNotFoundError(f"Can not find file `{key_path}`")
        

    def _get_media_id(self,media_type:str,p_media:Path):
        """
        获取上传文件的id
    
        Args:
            file_path: 文件路径
            media_type: 媒体类型（image/video/voice等）
        
        Returns:
            文件上传数据结构
        """
        files = {
            (None, (p_media.name, p_media.open('rb'), f'{media_type}/{p_media.suffix[1:]}'))
        }
        res = requests.post(self._media_api,files=files).json()
        if res.get('errcode') == 0:
            self.logger.info("获取media 成功")
        else:
            self.logger.info(f"获取media 失败，原因:{res.get('errmsg')}")

        return res
    


    def _post(self, url: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """
        发送消息的统一方法，要求utf-8编码
        
        Args:
            url: 请求URL
            data: 要发送的数据字典
            
        Returns:
            响应结果字典，如果发生错误返回None
            
        Raises:
            HTTPError: HTTP请求错误
            ConnectionError: 连接错误
            Timeout: 请求超时
            RequestException: 其他请求异常
        """
        # 1. 处理消息频率限制
        self._handle_rate_limit()
        
        try:
            # 2. 准备请求数据
            post_data = json.dumps(data, ensure_ascii=False)
            self.logger.info(f'请求参数为{post_data}')
            # 3. 发送请求
            response = self._send_request(url, post_data)
            
            # 4. 处理响应
            return self._handle_response(response)
            
        except (HTTPError, ConnectionError, Timeout, RequestException) as e:
            self._handle_request_error(e)
            raise
            
    def _handle_rate_limit(self) -> None:
        """处理消息发送频率限制"""
        now = time.time()
        self.queue.put(now)
        
        if self.queue.full():
            interval_time = now - self.queue.get()
            if interval_time < 60:
                sleep_time = int(60 - interval_time) + 1
                self.logger.info(f'机器人每分钟限制20条消息，当前已超出限制，需等待{sleep_time}s')
                time.sleep(sleep_time)
                
    def _send_request(self, url: str, data: str) -> requests.Response:
        """发送HTTP请求"""
        return requests.post(
            url,
            headers=self.headers,
            data=data,
            proxies=self.proxies,
            timeout=30  # 添加超时设置
        )
        
    def _handle_response(self, response: requests.Response) -> Optional[Dict[str, Any]]:
        """处理HTTP响应"""
        try:
            result = response.json()
            if result.get('errcode') == 0:
                self.logger.info('消息发送成功')
            else:
                self.logger.warning(f'消息发送返回错误码: {result.get("errcode")}, 错误信息: {result.get("errmsg")}')
            return result
        except json.JSONDecodeError:
            self.logger.error(
                f'服务器响应异常，状态码：{response.status_code}，响应内容：{response.text}'
            )
            return None
            
    def _handle_request_error(self, error: Exception) -> None:
        """处理请求错误"""
        error_messages = {
            HTTPError: lambda e: f"发送失败，HTTP error: {e.response.status_code}, 原因: {e.response.reason}",
            ConnectionError: lambda _: "发送失败，HTTP connection error!",
            Timeout: lambda _: "发送失败，Timeout error!",
            RequestException: lambda _: "发送失败，Request Exception!"
        }
        
        error_type = type(error)
        if error_type in error_messages:
            self.logger.error(error_messages[error_type](error))
        else:
            self.logger.error(f"发送失败，未知错误: {str(error)}")

    def _send(
        self,
        msg_type: str,
        data: Dict[str, Any],
        media_path: Optional[str] = '',
        **kwargs: Any
    ) -> Dict[str, Any]:
        """
        发送消息的统一方法

        Args:
            msg_type: 消息类型
            data: 消息数据
            media_path: 媒体文件路径
            **kwargs: 其他参数

        Returns:
            发送结果

        Raises:
            MessageError: 消息发送失败时
        """
        data['msgtype'] = msg_type

        if msg_type == 'image':
            data['image'] = self._prepare_image_data(media_path)
        elif msg_type == 'markdown':
            data['markdown']['content'] = self._prepare_markdown_content(
                data['markdown']['content']
            )
        elif msg_type == 'file':
            media_res = self._get_media_id(
                media_type='file',
                p_media=Path(media_path)
            )
            data[msg_type]['media_id'] = media_res.get('media_id', '')

        # 打印发送的消息内容
        self.logger.setLevel(logging.INFO)
        self.logger.info(f"准备发送消息: {json.dumps(data, ensure_ascii=False, indent=2)}")
        
        return self._post(self.webhook_send_api, data)

